/*
 * =====================================================================================
 *
 *       Filename:  config.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2020年06月11日 14时04分56秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */

#include <uci.h>
#include "config.h"
#include "util_net.h"
#include "util_debug.h"

/*
 * return:
 *  -3: no such option
 *  -2: not such section
 *  -1: failed
 *   0: ok
 */
int util_uci_get(const char *package, const char *section, const char *option, char *value, int len)
{
    int ret = -1;
    static struct uci_context *ctx;
    struct uci_package *pkg;
    struct uci_element *e = NULL;

    ctx = uci_alloc_context();
    if(UCI_OK != uci_load(ctx, package, &pkg)) {
        debug_err("%s(): uci_load(%s) failed\n", __func__, package);
        goto cleanup;
    }

    struct uci_section *s = uci_lookup_section(ctx, pkg, section);
    if(s) {
        const char *v = uci_lookup_option_string(ctx, s, option);
        if(NULL == v) {
            debug_err("%s(): In %s.%s No Option(%s)\n", __func__, package, section, option);
            ret = -3;
            goto cleanup2;
        }

        debug_print("%s(): %s.%s.%s=%s\n", __func__, package, section, option, v);
        strncpy(value, v, len);
        ret = 0;
    }
    else {
        debug_err("%s(): In %s No Section(%s)\n", __func__, package, section);
        ret = -2;
    }

cleanup2:
    uci_unload(ctx, pkg);
cleanup:
    uci_free_context(ctx);

    ctx = NULL;
    return ret;

}

/*
 * return: 
 * 0: ok
 * -1: failed
 */
int util_uci_set(const char *package, const char *section, const char *option, char *value)
{
    int ret = -1;
    char *config = package;
    struct uci_context *ctx;
    struct uci_package *pkg;
    struct uci_element *e = NULL;

    ctx = uci_alloc_context();
    if(NULL == ctx) {
        debug_err("%s(): uci_alloc_context() failed\n", __func__);
        return -1;
    }
    /* uci_load: lock file with sharing lock */
    if(uci_load(ctx, config, &pkg) != UCI_OK) {
        debug_err("%s(): uci_load(%s) failed\n", __func__, config);
        ret = -1;
        goto cleanup;
    }

    struct uci_ptr ptr;
    struct uci_section *s = uci_lookup_section(ctx, pkg, section);
    if(s) {
        memset(&ptr, 0, sizeof(ptr));
        ptr.p = pkg;
        ptr.s = s;
        /* 这个设置为NULL是不是意味着添加?Yes */
        ptr.o = uci_lookup_option(ctx, s, option);

        ptr.package = pkg->e.name;
        ptr.section = s->e.name;


        ptr.option = option;
        ptr.value = value;
        debug_print("%s(): %s.%s.%s=%s\n", __func__, package, section, option, value);
        uci_set(ctx, &ptr);
        ret = 0;
    }
    else {
        debug_err("%s(): In %s No Section(%s)\n", __func__, package, section);
        ret = -2;
    }

    uci_save(ctx, pkg);
    /* uci_commit: lock file with exclusive lock */
    uci_commit(ctx, &pkg, 1);

    uci_unload(ctx, pkg);
cleanup:
    uci_free_context(ctx);
    return ret;
}

void hsion_mLink_inteface_print(struct mLink_interface *interface)
{
    debug_print("Net:%s, metric:%u, up:%u, down:%u, count:%u, weight:%u, timeout:%u, interval:%u, track_status:%u",
            interface->net, interface->metric, interface->up, interface->down, interface->count,
            interface->timeout, interface->interval, interface->track_status);
    struct list_head *pos;
    debug_print(", track_ip:");
    list_for_each(pos, &(interface->head_track_ip)) {
        struct track_ip *entry = list_entry(pos, struct track_ip, list);
        debug_print(IP_STR_FMT, IP_STR_ARG(&(entry->ip)));
        if(pos->next != &(interface->head_track_ip)) {
            debug_print("/");
        }
    }
    debug_print("\n");
}

static int hsion_mLink_parse_track_ip(struct uci_option *o, struct list_head *head)
{
    int ret = 0;
    if(o) {
        switch(o->type) {
        case UCI_TYPE_STRING:
            {
                printf("%s\n", o->v.string);
                break;
            }
        case UCI_TYPE_LIST:
            {
                struct uci_element *e;
                uci_foreach_element(&o->v.list, e) {
                    printf("%s ",e->name);
                    struct track_ip *entry = (struct track_ip *)calloc(1, sizeof(*entry));
                    if(NULL == entry) {
                        DEBUG_ERR(__func__, "calloc");
                        ret = -1;
                        break;
                    }
                    entry->ip = inet_addr(e->name);
                    list_add_tail(&(entry->list), head);
                }
                printf("\n");
                break;
            }
        default:
            printf("<unknown>\n");
            break;
        }
    }
    return ret;
}

int hsion_mLink_config_init(struct list_head *head, int *enabled)
{
    int ret = -1;
    char *file = "hsion_mLink";
    static struct uci_context *ctx;
    struct uci_package *pkg;
    struct uci_element *e = NULL;
    *enabled = 0;

    ctx = uci_alloc_context();
    if(UCI_OK != uci_load(ctx, file, &pkg)) {
        printf("uci_load(%s) not ok\n", file);
        goto cleanup;
    }
    uci_foreach_element(&pkg->sections, e) {
        struct uci_section *s = uci_to_section(e);
        if(0 == strcmp(s->type, "general")) {
            const char *str_enabled = uci_lookup_option_string(ctx, s, "enabled");
            if(NULL == str_enabled) {
                debug_err("%s(): general.enabled is not defined\n", __func__);
                goto cleanup2;
            }
            *enabled = atoi(str_enabled);
        }
        else if(0 == strcmp(s->type, "interface")) {
            const char *net = uci_lookup_option_string(ctx, s, "net");
            const char *up = uci_lookup_option_string(ctx, s, "up");
            const char *down = uci_lookup_option_string(ctx, s, "down");
            const char *count = uci_lookup_option_string(ctx, s, "count");
            const char *weight = uci_lookup_option_string(ctx, s, "weight");
            const char *timeout = uci_lookup_option_string(ctx, s, "timeout");
            const char *interval = uci_lookup_option_string(ctx, s, "interval");
            const char *track_status_init = uci_lookup_option_string(ctx, s, "track_status_init");
            struct uci_option *track_ip = uci_lookup_option(ctx, s, "track_ip");
            if(NULL == net || NULL == track_ip) {
                debug_err("%s(): Lack of 'interface.net' or 'interface.track_ip'\n", __func__);
                goto cleanup2;
            }

            char str_metric[30] = {0};
            if(util_uci_get("network", net, "metric", str_metric, sizeof(str_metric)) < 0) {
                debug_err("%s(): get interface(%s) metric failed\n", __func__, net);
                goto cleanup2;
            }

            struct mLink_interface *entry = (struct mLink_interface *)calloc(1, sizeof(*entry));
            if(NULL == entry) {
                DEBUG_ERR(__func__, "calloc");
                goto cleanup2;
            }

            entry->metric = atoi(str_metric);
            strncpy(entry->net, net, sizeof(entry->net));
            strncpy(entry->uci_sec_name, s->e.name, sizeof(entry->uci_sec_name)); 
            INIT_LIST_HEAD(&(entry->head_track_ip));
            hsion_mLink_parse_track_ip(track_ip, &(entry->head_track_ip));

            if(up) {
                entry->up = atoi(up);
            }
            else {
                entry->up = HSION_MLINK_DEFAULT_UP;
            }

            if(down) {
                entry->down = atoi(down);
            }
            else {
                entry->down = HSION_MLINK_DEFAULT_DOWN;
            }

            if(count) {
                entry->count = atoi(count);
            }
            else {
                entry->count = HSION_MLINK_DEFAULT_COUNT;
            }

            if(weight) {
                entry->weight = atoi(weight);
            }
            else {
                entry->weight = HSION_MLINK_DEFAULT_WEIGHT;
            }

            if(timeout) {
                entry->timeout = atoi(timeout);
            }
            else {
                entry->timeout = HSION_MLINK_DEFAULT_TIMEOUT;
            }

            if(interval) {
                entry->interval = atoi(interval);
            }
            else {
                entry->interval = HSION_MLINK_DEFAULT_INTERVAL;
            }
            if(track_status_init) {
                entry->track_status = atoi(track_status_init);
            }
            else {
                /* 默认认为down */
                entry->track_status = TRACK_STATUS_DOWN;
            }

            hsion_mLink_inteface_print(entry);
            list_add_tail(&(entry->list), head);
        }
    }

    ret = 0;
cleanup2:
    uci_unload(ctx, pkg);
cleanup:
    uci_free_context(ctx);

    ctx = NULL;
    return ret;
}

